/*
Copyright 2023-present The maxGraph project Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { describe, expect, test } from '@jest/globals';
import { createGraphWithoutContainer } from '../../utils';
import { type Cell, type CellStyle, Geometry, type Graph } from '../../../src';

const expectIsVertex = (cell: Cell, cellId: string) => {
  expect(cell.getId()).toBe(cellId);
  expect(cell.vertex).toBeTruthy();
  expect(cell.edge).toBeFalsy();
};

const expectCellInModel = (graph: Graph, expectedCell: Cell) => {
  const cellFromModel = graph.getDataModel().getCell(expectedCell.getId()!);
  expect(cellFromModel).toBe(expectedCell);
};

const expectIsTheOnlyChildOfDefaultParent = (cell: Cell) => {
  expect(cell.parent).not.toBeNull();
  expect(cell.parent?.id).toBe('1'); // default parent
  const children = cell.parent?.children;
  expect(children).toContain(cell);
  expect(children).toHaveLength(1);
};

const isTheOnlyChildOf = (parentCell: Cell, childCell: Cell) => {
  expect(childCell.parent).toBe(parentCell);
  const children = parentCell.children;
  expect(children).toContain(childCell);
  expect(children).toHaveLength(1);
};

const nonRelativeGeometry = (x: number, y: number, width: number, height: number) => {
  const geometry = new Geometry(x, y, width, height);
  geometry.relative = false;
  return geometry;
};

const relativeGeometry = (x: number, y: number, width: number, height: number) => {
  const geometry = new Geometry(x, y, width, height);
  geometry.relative = true;
  return geometry;
};

describe('insertVertex', () => {
  describe('Legacy method (with several parameters)', () => {
    test('with position, size and style', () => {
      const graph = createGraphWithoutContainer();
      const style: CellStyle = { rounded: true, shape: 'cloud' };
      const cellId = 'vertex_1';
      const cell = graph.insertVertex(null, cellId, 'a value', 10, 20, 110, 120, style);

      expectIsVertex(cell, cellId);
      expect(cell.value).toBe('a value');
      expect(cell.style).toStrictEqual(style);
      expect(cell.geometry).toStrictEqual(nonRelativeGeometry(10, 20, 110, 120));

      expectIsTheOnlyChildOfDefaultParent(cell);
      expectCellInModel(graph, cell);
    });

    test('with no position nor size', () => {
      const graph = createGraphWithoutContainer();
      const cellId = 'noPositionNorSize_';
      const cell = graph.insertVertex(
        null,
        cellId,
        'a value for cell without position and size'
      );

      expectIsVertex(cell, cellId);
      expect(cell.value).toBe('a value for cell without position and size');
      expect(cell.style).toStrictEqual({});
      expect(cell.geometry).toStrictEqual(nonRelativeGeometry(0, 0, 0, 0));

      expectIsTheOnlyChildOfDefaultParent(cell);
      expectCellInModel(graph, cell);
    });
  });

  describe('with single parameter', () => {
    const autoGeneratedCellId = '2';

    test('with position, size and style', () => {
      const graph = createGraphWithoutContainer();
      const style: CellStyle = { align: 'right', fillColor: 'red' };
      const cell = graph.insertVertex({
        value: 'another value',
        x: 10,
        y: 20,
        size: [110, 120],
        style,
      });

      expectIsVertex(cell, autoGeneratedCellId);
      expect(cell.value).toBe('another value');
      expect(cell.style).toStrictEqual(style);
      expect(cell.geometry).toStrictEqual(nonRelativeGeometry(10, 20, 110, 120));

      expectIsTheOnlyChildOfDefaultParent(cell);
      expectCellInModel(graph, cell);
    });

    test('with non default parent', () => {
      const graph = createGraphWithoutContainer();

      const parentCell = graph.insertVertex({
        value: 'non default',
        position: [10, 10],
        size: [400, 400],
      });
      expectIsVertex(parentCell, autoGeneratedCellId);
      expect(parentCell.value).toBe('non default');
      expect(parentCell.style).toStrictEqual({});
      expect(parentCell.geometry).toStrictEqual(nonRelativeGeometry(10, 10, 400, 400));
      expectIsTheOnlyChildOfDefaultParent(parentCell);
      expectCellInModel(graph, parentCell);

      const childCellId = 'childId';
      const childCell = graph.insertVertex({
        parent: parentCell,
        id: childCellId,
        value: 'child',
        position: [5, 5],
        width: 400,
        height: 400,
        relative: true,
      });
      expectIsVertex(childCell, childCellId);
      expect(childCell.value).toBe('child');
      expect(childCell.style).toStrictEqual({});
      expect(childCell.geometry).toStrictEqual(relativeGeometry(5, 5, 400, 400));
      expectCellInModel(graph, childCell);
      isTheOnlyChildOf(parentCell, childCell);
    });

    test('with no position nor size', () => {
      const graph = createGraphWithoutContainer();
      const cellId = 'noPositionNorSize';
      const cell = graph.insertVertex({
        id: cellId,
        value: 'a value for cell without position and size',
      });
      expectIsVertex(cell, cellId);
      expect(cell.value).toBe('a value for cell without position and size');
      expect(cell.style).toStrictEqual({});
      expect(cell.geometry).toStrictEqual(nonRelativeGeometry(0, 0, 0, 0));

      expectIsTheOnlyChildOfDefaultParent(cell);
      expectCellInModel(graph, cell);
    });
  });
});
